package fieldmaps.files;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import com.vividsolutions.jts.geom.Coordinate;

import fieldmaps.database.DatabaseGenericMap;
import fieldmaps.database.DatabaseRasterMap;
import fieldmaps.types.RasterMapInfo;

public class LoadRasterMapFromFile implements ILoadMapFromFile { 

	private Map<String, String> properties;
	
	public LoadRasterMapFromFile() {
		this.properties = new HashMap<String, String>();
	}

	public DatabaseGenericMap createMapFromFile (String filename, String mapName) throws IOException {
		RasterMapInfo mapInfo = this.readHeader(filename + ".hdr");
		DatabaseGenericMap map = DatabaseRasterMap.createMap(mapName, mapInfo);
		readData(map, mapInfo, filename + ".bil");
		return map;
	}
	
	private RasterMapInfo readHeader (String filename) throws IOException {
		
		RasterMapInfo mapInfo = new RasterMapInfo();
		
		FileInputStream file = new FileInputStream(filename);
		InputStreamReader stream = new InputStreamReader(file);
		BufferedReader reader = new BufferedReader(stream);
		String line = null;

		while ((line = reader.readLine()) != null) {
			String[] values = line.split(" +");
			if(values.length > 1) {
				properties.put(values[0], values[1]);
			}
		}

		System.out.println("Parsing the Header...");
		
		mapInfo.setByteOrder (properties.get("BYTEORDER").charAt(0));
		mapInfo.setLayout (properties.get("LAYOUT"));
		mapInfo.setNrows (Integer.valueOf(properties.get("NROWS")));
		mapInfo.setNcols (Integer.valueOf(properties.get("NCOLS")));
		mapInfo.setNbands (Integer.valueOf(properties.get("NBANDS")));
		mapInfo.setNbits (Integer.valueOf(properties.get("NBITS")));
		mapInfo.setBandrowbytes (Integer.valueOf(properties.get("BANDROWBYTES")));
		mapInfo.setTotalrowbytes (Integer.valueOf(properties.get("TOTALROWBYTES")));
		mapInfo.setBandgapbytes (Integer.valueOf(properties.get("BANDGAPBYTES")));
		mapInfo.setNodata (Integer.valueOf(properties.get("NODATA")));
		mapInfo.setUlxmap (Double.valueOf(properties.get("ULXMAP")));
		mapInfo.setUlymap (Double.valueOf(properties.get("ULYMAP")));
		mapInfo.setXdim (Double.valueOf(properties.get("XDIM")));
		mapInfo.setYdim (Double.valueOf(properties.get("YDIM")));
		
		return mapInfo;
	}

	private void readData (DatabaseGenericMap map, RasterMapInfo mapInfo, String filename) throws IOException {

		char byteOrder = mapInfo.getByteOrder();
		int nodata = mapInfo.getNodata();
		int nbits = mapInfo.getNbits();
		int nrows = mapInfo.getNrows();
		int ncols = mapInfo.getNcols();

		if (byteOrder != 'I') {
			throw new IOException("The data must be represented little endian.");
		}

		System.out.println("Reading the data from file...");

		FileInputStream datafile = new FileInputStream(filename);
		DataInputStream stream = new DataInputStream(datafile);

		int cantbytes = nbits/8;
		int cellNumber = 0;

		for (int i = 0; i < nrows; i++) {
			for (int j = 0; j < ncols; j++) {

				ByteBuffer buffer = ByteBuffer.allocate(cantbytes);
				buffer.order(ByteOrder.LITTLE_ENDIAN);
				for (int k = 0; k < cantbytes; k ++) {
					buffer.put(stream.readByte());
				}
				buffer.rewind();
				int value = buffer.getShort();
				if (value != nodata) {
					Coordinate[] coordenates = getCellCoordenates(mapInfo, cellNumber);
					try {
						map.addCell(coordenates, value);
					} catch (SQLException e) {
						System.out.println("Error al cargar el mapa en la base de datos.");
						System.exit(0);
					}
				}
				cellNumber++;
			}
		}

		System.out.println("---> The file was loaded ok!");
		System.out.println("");
	}

	private Coordinate rasterCellCenter(RasterMapInfo mapInfo, int cellNumber) {

		int nc = mapInfo.getNcols();
		double x = mapInfo.getUlxmap() + (cellNumber % nc) * mapInfo.getXdim();
		double y = mapInfo.getUlymap() - (cellNumber / nc) * mapInfo.getYdim();
		return new Coordinate(x, y);
	}

	private Coordinate[] getCellCoordenates(RasterMapInfo mapInfo, int cellNumber) {

		Coordinate center = this.rasterCellCenter(mapInfo, cellNumber);
		double xDistance = mapInfo.getXdim()/2;
		double yDistance = mapInfo.getYdim()/2;

		Coordinate p1 = new Coordinate();
		Coordinate p2 = new Coordinate();
		Coordinate p3 = new Coordinate();
		Coordinate p4 = new Coordinate();

		p1.x = center.x - xDistance;
		p1.y = center.y + yDistance;

		p2.x = center.x + xDistance;
		p2.y = center.y + yDistance;
		
		p3.x = center.x + xDistance;
		p3.y = center.y - yDistance;
		
		p4.x = center.x - xDistance;
		p4.y = center.y - yDistance;
		
		return new Coordinate[] { p1, p2, p3, p4, p1 };
	}

}
